#pragma once
#include <iostream>
#include <cstring>
#include <string>
#include <cerrno>
#include <sys/types.h>  // ::socket
#include <sys/socket.h>
#include <netinet/in.h> // ::bind
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <functional>

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

#define BACKLOG 8
using namespace LogMudule;
using namespace ThreadPoolModule;
static const uint16_t gport = 8080;

class Tcpserver
{
    using task_t = std::function<void()>;
    // 解决多线程版本 传递参数 问题
    struct ThreadData
    {
        int sockfd;
        Tcpserver *self;
    };
public:
    Tcpserver(uint16_t port = gport)
        : _port(port)
        , _isrunning(false)
    {}
    // 初始化TCP服务器
    void InitServer()
    {
        // 1. 创建tcp socket
        //                 默认网络编程，   流式套接字
        _listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if(_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket create success, sockfd is : " << _listensockfd;

        // 2. bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        // 填充字段
        local.sin_family = AF_INET;
        local.sin_port = htons(gport);  // 需要经过网络发送给对端，所以将端口号转为网络序列
        local.sin_addr.s_addr = INADDR_ANY; // 绑定任意ip
        
        int n = ::bind(_listensockfd, CONV(&local), sizeof(local));
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success, sockfd is : " << _listensockfd;

        // 3. client -> server, tcp是面向连接的，就要求tcp服务器随时随地等待被连接
        // tcp 需要将socket设置成为监听状态，即随时等待别人来链接我。tcp 特有的
        n = ::listen(_listensockfd, BACKLOG); // 设置监听状态
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "listen error";
            Die(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success, sockfd is : " << _listensockfd;
        //::signal(SIGCHLD, SIG_IGN); // 子进程退出，OS会自动回收资源，不用在wait了
    }
    // 启动TCP服务器
    void Start()
    {
        _isrunning = true;
        while(_isrunning)
        {
            // TCP不能直接读取数据
            // 1. 获取新连接
            struct sockaddr_in peer;
            socklen_t peerlen = sizeof(peer);
            LOG(LogLevel::DEBUG) << "accept ing ...";
            // 我们要获取client的信息：数据(通过sockfd) + client socket信息(通过 accept || recvfrom)
            // 服务器通过accept获取到客户端的连接请求
            int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);     // 重点
            if(sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error: " << strerror(errno);
                continue;
            }

            // 走到这里说明获取连接成功了
            LOG(LogLevel::INFO) << "accept success, sockfd is : " << sockfd;
            InetAddr addr(peer);
            LOG(LogLevel::INFO) << "client info: " << addr.Addr();

            // // version 2  ----  多线程版本
            // // 主线程和新线程是如何看待：文件描述符表，共享一张文件描述符表！！
            // pthread_t tid;
            // // 在堆上单独为sockfd开辟空间，避免在该线程还没有执行，主线程那就接收了一个新的accept
            // ThreadData *data = new ThreadData;  
            // data->sockfd = sockfd;
            // data->self = this;
            // pthread_create(&tid, nullptr, ThreadEntry, data);

            // version-3：线程池版本 比较适合处理短任务，或者是用户量少的情况
            // function对象作为任务
            task_t f = std::bind(&Tcpserver::HandlerRequest, this, sockfd); // 构建任务
            ThreadPool<task_t>::getInstance()->Equeue(f); // 加入线程池

            // lambda表达式 需要把Equeue函数参数的引用去掉
            // ThreadPool<task_t>::getInstance()->Equeue([this, sockfd]()
            //                                           { this->HandlerRequest(sockfd); 
        }
    }
    // 不能这样设计函数，因为在类内，包含this指针，与pthread_create的参数不匹配，得加static
    // void *ThreadEntry(void *args)    多线程版本, 需要用到的函数
    static void *ThreadEntry(void *args)
    {
        // int sockfd = *(int *)args;
        // HandlerRequest(sockfd); // 静态成员函数不能调用非静态成员函数
        pthread_detach(pthread_self()); // 线程分离，主线程不等待子线程
        ThreadData *data = (ThreadData *)args;
        data->self->HandlerRequest(data->sockfd);
        return nullptr;
    }
    void HandlerRequest(int sockfd)
    {
        LOG(LogLevel::INFO) << "HandlerRequest, sockfd is : " << sockfd;
        char inbuffer[4096];
        while(true)
        {
            // 从socket中读取数据，读取到inbuffer中
            // ssize_t n = ::read(sockfd, inbuffer, sizeof(inbuffer) - 1);  // 读取不完善
            ssize_t n = ::recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0);  // 使用recv读取
            if(n > 0)   // 读成功
            {
                // 服务器显示的消息
                LOG(LogLevel::INFO) << inbuffer;
                inbuffer[n] = 0;
                std::string echo_str = "server echo# ";
                echo_str += inbuffer;
                // 服务器把读到的消息处理后，发送到socket，客户端再读取，消息就回显到了客户端
                // ::write(sockfd, echo_str.c_str(), echo_str.size());     // 写入不完善
                ::send(sockfd, echo_str.c_str(), echo_str.size(), 0);      // 使用send写入
            }
            else if (n == 0)
            {
                // read 如果读取返回值是0，表示client退出
                LOG(LogLevel::INFO) << "client quit: " << sockfd;
                break;
            }
            else
            {
                // 读取失败了
                break;
            }
        }
        ::close(sockfd); // fd泄漏问题！
    }
    // 关闭TCP服务器
    void Stop()
    {
        _isrunning = false;
    }
    ~Tcpserver()
    {}
private:
    int _listensockfd;  // 监听socket
    uint16_t _port;
    bool _isrunning;    // TCP服务器状态
};